/**
 * Mantenere separata la descrizione dell'interfaccia di controllo da quella delle altre?
 */
package neptune.manager.model.topology;

import java.util.ArrayList;

/**
 * @author Roberto Bifulco [info@robertobifulco.it, robertobifulco.it]
 * 
 */
public class Node {
	
	/**
	 * I possibili stati di un nodo
	 * 
	 * @author Roberto Bifulco [RobertoBifulco.it] [info@robertoBifulco.it]
	 *
	 */
	public static enum Status{
		NOSTATE, RUNNING, BLOCKED, PAUSED, SHUTDOWN, SHUTOFF, CRASHED;
	}

	/**
	 * Descrizione testuale a scopo documentale
	 */
	private String description;
	
	/**
	 * Il nome del nodo
	 */
	private String name;
	
	/**
	 * Id univoco del nodo
	 */
	private String id;
	/**
	 * Una stringa che indica il tipo di cpu
	 */
	private Cpu cpu;
	/**
	 * la ram del nodo espressa in MB
	 */
	private int ram;
	/**
	 * lo spazio su disco espresso in GB
	 */
	private float hd;
	/**
	 * Le interfacce di rete possedute da questo nodo
	 */
	private ArrayList<NetworkInterface> interfaces = new ArrayList<NetworkInterface>(
			0);
	/**
	 * La sigla del sistema operativo installato sul nodo
	 */
	private OperativeSystem operativeSystem;
	/**
	 * L'identificativo del software template usato da questo nodo
	 */
	private String softwareTemplate;
	/**
	 * Le componenti software installate sul nodo
	 */
	private ArrayList<Software> installedSoftwares = new ArrayList<Software>(0);
	/**
	 * L'eventuale hypervisor in esecuzione sul nodo
	 */
	private Hypervisor hypervisor;
	/**
	 * Gli eventuali nodi virtuali opsitati su questo nodo
	 */
	protected ArrayList<String> hostedNodes = new ArrayList<String>(0);
	/**
	 * Specifica se questo nodo � fisico (false) o se e' virtuale e quindi
	 * ospitato da un altro nodo (true)
	 */
	private boolean isVirtual;
	/**
	 * Indica l'hoster per questo nodo, null se il nodo non e' ospitato da
	 * nessun nodo, perche' non allocato o perche' nodo fisico.
	 */
	private Node hoster;
	/**
	 * La porta su cui e' in ascolto il server ssh per controllare questo nodo
	 */
	private int controlPort = 22;

	/**
	 * @param description
	 * @param id
	 */
	public Node(String description, String id, String name)
	{
		// TODO gestire univocita' id
		this.description = description;
		this.id = id;
		this.name=name;

		interfaces = new ArrayList<NetworkInterface>(0);
		installedSoftwares = new ArrayList<Software>(0);
	}

	/**
	 * Aggiunge un nodo virtuale a questo nodo. Se non ci sono sufficienti
	 * risorse lancia un'eccezione.
	 * 
	 * Al nodo passato, in caso di successo nella procedura di hosting, viene
	 * impostato il campo hoster opportunamente.
	 * 
	 * @param node
	 * @throws NotEnoughResourcesException
	 */
	public void addHostedNode(Node node) throws NotEnoughResourcesException {
		if (verifyHostingCapacity(node)) {
			hostedNodes.add(node.getId());
			node.setHoster(this);
		} else
			throw new NotEnoughResourcesException("The node \"" + this.getId()
					+ "\" has not enough resources to host virtual node \""
					+ node.getId() + "\"");
	}

	public void addNetworkInterface(NetworkInterface netInterface) {
		if (netInterface == null)
			return;
		netInterface.setParentNode(id);
		interfaces.add(netInterface);
	}

	public void addSoftware(Software software) {
		installedSoftwares.add(software);
	}

	/**
	 * Restituisce l'indirizzo ip di controllo per questo nodo
	 * 
	 * @return
	 * @throws UndefinedControlInterfaceException
	 */
	public IPAddress getControlAddress()
			throws UndefinedControlInterfaceException {
		for (NetworkInterface actualInterface : interfaces) {
			if (actualInterface.hasControlAddress())
				return actualInterface.getControlAddress();
		}
		throw new UndefinedControlInterfaceException(
				"No control interfaces defined for node \"" + id + "\"");
	}

	public int getControlPort() {
		return controlPort;
	}

	public Cpu getCpu() {
		return cpu;
	}

	public String getDescription() {
		return description;
	}

	public float getHd() {
		return hd;
	}

	public String[] getHostedNodes() {
		return (String[]) hostedNodes.toArray(new String[0]);
	}

	/**
	 * @return the hoster
	 */
	public Node getHoster() {
		return hoster;
	}

	public Hypervisor getHypervisor() {
		return hypervisor;
	}

	public String getId() {
		return id;
	}

	public Software[] getInstalledSoftwares() {
		return (Software[]) installedSoftwares.toArray(new Software[0]);
	}

	/**
	 * Restituisce l'interfaccia con il dato nome, o null se non esiste su
	 * questo nodo.
	 * 
	 * @param interfaceName
	 * @return
	 */
	public NetworkInterface getInterfaceByName(String interfaceName) {
		for (NetworkInterface netIf : interfaces) {
			if (netIf.getInterfaceName().equals(interfaceName))
				return netIf;
		}

		return null;
	}

	public NetworkInterface[] getInterfaces() {
		return (NetworkInterface[]) interfaces.toArray(new NetworkInterface[0]);
	}

	public String getName() {
		return name;
	}

	public OperativeSystem getOperativeSystem() {
		return operativeSystem;
	}

	public int getRam() {
		return ram;
	}

	public String getSoftwareTemplate() {
		return softwareTemplate;
	}

	/**
	 * Verifica se a questo nodo corrisponde un determinato indirizzo(MAC o IP)
	 * 
	 * @param address
	 * @return
	 */
	public boolean hasAddress(Address address) {
		for (NetworkInterface actualInterface : interfaces) {
			if (actualInterface.hasAddress(address))
				return true;
		}
		return false;
	}

	/**
	 * Verifica se a questo nodo corrisponde un determinato indirizzo(MAC o IP)
	 * appartenente soltanto ad una rete di topologia e non di controllo
	 * 
	 * @param address
	 * @return
	 */
	public boolean hasTopologyAddress(Address address) {
		for (NetworkInterface actualInterface : interfaces) {
			if (actualInterface.hasAddress(address)) {
				if (address instanceof IPAddress) {
					if (((IPAddress) actualInterface.getAddress(address))
							.isControlAddress())
						return false; // Se l'indirizzo trovato e' di
					// controllo, non puo' esserci un altro
					// indirizzo uguale che non sia di
					// controllo
				}

				return true;
			}

		}
		return false;
	}

	/**
	 * Verifica se un nodo e' presente fra i nodi ospitati direttamente da
	 * questo nodo.
	 * 
	 * @param nodeId
	 * @return
	 */
	public boolean isChildren(String nodeId) {
		for (String actualId : hostedNodes) {
			if (actualId.equals(nodeId))
				return true;
		}

		return false;
	}

	/**
	 * @return the isVirtual
	 */
	public boolean isVirtual() {
		return isVirtual;
	}

	/**
	 * Rimuove un nodo virtuale ospitato su questa macchina. Se il nodo non e'
	 * ospitato su questa macchina, non fa nulla.
	 * 
	 * @param node
	 */
	public void removeHostedNode(Node node) {
		hostedNodes.remove(node.getId());
	}

	public void setControlPort(int controlPort) {
		this.controlPort = controlPort;
	}

	public void setCpu(Cpu cpu) {
		this.cpu = cpu;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public void setHd(float hd) {
		if (hd < 0)
			hd = 0;
		this.hd = hd;
	}

	/**
	 * @param hoster
	 *            the hoster to set
	 */
	public void setHoster(Node hoster) {
		this.hoster = hoster;
	}

	public void setHypervisor(Hypervisor hypervisor) {
		this.hypervisor = hypervisor;
	}

	public void setId(String id) {
		// TODO aggiungere verifica univocita'
		this.id = id;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setOperativeSystem(OperativeSystem operativeSystem) {
		this.operativeSystem = operativeSystem;
	}

	public void setRam(int ram) {
		if (ram < 0)
			ram = 0;
		this.ram = ram;
	}

	public void setSoftwareTemplate(String softwareTemplate) {
		this.softwareTemplate = softwareTemplate;
	}

	/**
	 * @param isVirtual
	 *            the isVirtual to set
	 */
	public void setVirtual(boolean isVirtual) {
		this.isVirtual = isVirtual;
	}

	/**
	 * Verifica se ci sono sufficienti risorse su questo nodo, per ospitare il
	 * nodo virtuale passato come parametro
	 * 
	 * @param node
	 * @return
	 */
	public boolean verifyHostingCapacity(Node node) {
		// TODO Da verificare come implementarlo
		return true;
	}
}
